﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

using RRRSRoguelike.Entities;
using RRRSRoguelike.Entities.Props;
using RRRSRoguelike.Enums;
using RRRSRoguelike.Helpers;
using RRRSRoguelike.Helpers.Actions;
using RRRSRoguelike.Helpers.Visualizer;
using RRRSRoguelike.Managers;
using RRRSRoguelikeData;

namespace RRRSRoguelike.Screens
{
    /// <summary>
    /// Contains the main game logic
    /// </summary>
    public abstract class DungeonScreen:Screen
    {

        #region variables             
            
            //used to make manager calls shorter
            private Player player;
            
            protected int seed;
            
            private bool processMonsters;
            private bool Exit;

        # endregion

        #region constructors
        
        public DungeonScreen(int seed = -1)
        {
        	
        	SetSeed(seed);
        }


        #endregion

        #region methods

        protected virtual void StartGame(int seed = -1)
        {
        	   
            //Screen within a screen - later each screen may need its own screen stack
            TextScreen s = new TextScreen(DungeonManager.Instance.Texts["Welcome"]);
            s.Show();

            player = DungeonManager.Instance.CreatureManager.Player;
            
            ResetBools();
            
            InitialDraw();

            //Hook up events. resets old uses forcing HUD redraw on weapon change.
            DungeonManager.Instance.CreatureManager.Player.WeaponChanged += resetOldUses;
            
            //-----------------------------------------------------------------
            MainGameLoop();
            //-----------------------------------------------------------------
        }
        
        private long lastCall;
		private long delay = 16;
		private void MainGameLoop()
        {
			//Limit calls per second
        	if(GameManager.Instance.Time.ElapsedMilliseconds>lastCall+delay)
			{
				lastCall = GameManager.Instance.Time.ElapsedMilliseconds;
				
            while (DungeonManager.Instance.IsGameActive)
            {
                
                //read input                
                ProcessInput();
                if (Exit)
                	return;

                //do logic
                DoLogic();
                

                //Draw
                DoDraw();

            }
        	}          
            
            GameManager.Instance.ScreenStack.Push(new ReplayScreen(this));
            AddVictoryScreen();           
            
        }
        
        public void GetInput()
        {
            GameManager.Instance.InputManager.ReadKeys();   
        }

        public void DoLogic()
        {
            //Can later be part of the prop manager perhaps or the fireball itself
            Fireball fireball = DungeonManager.Instance.PropManager.Fireball;
            if (fireball.Active)
                ProcessFireball(fireball);

            // does things like auto pick up sword and take stairs
            if (player.hasMoved)
                CheckNewTile();

            if (processMonsters)
            {
            	
                DungeonManager.Instance.CreatureManager.ProcessMonsters();
            }
        }

        private void DoDraw()
        {
            if (drawPanels)
            {
                foreach (Panel p in PanelList)
                    p.Draw();

                drawPanels = false;
            }
            DisplayHUD();

            DungeonManager.Instance.DrawDungeon();
        }

        bool ShowBuildMenu = false;
        void SetupMenu(Menu menu)
		{
			menu.Add(new MenuItem("Wall",new EventHandler(SelectWall)));
        	menu.Add(new MenuItem("Door",new EventHandler(SelectDoor)));
        	menu.Add(new MenuItem("Remove",new EventHandler(SelectRemove)));
        	menu.Add(new MenuItem("Cancel",new EventHandler(SelectCancel)));
		}
        
        //Event method
        void SelectWall(object sender,EventArgs e)
        {
        	PanelList[(int)Panels.Left].Clear();
        	ActionHelper.Build(new Tile(player.Position,
            				DungeonManager.Instance.Dungeon.Data.Tiles["Wall"]));
        	ShowBuildMenu=false;
        	
        }
        
        //Event method
        void SelectDoor(object sender,EventArgs e)
        {
        	PanelList[(int)Panels.Left].Clear();
        	ActionHelper.Build(new Door(new Point(0,0)));
        	ShowBuildMenu = false;
        	
        }
        
        void SelectRemove(object sender,EventArgs e)
        {
        	PanelList[(int)Panels.Left].Clear();
        	ActionHelper.Remove();
        	ShowBuildMenu = false;
        	
        }
        
               //Event method
        void SelectCancel(object sender,EventArgs e)
        {
        	ShowBuildMenu = false;
        	PanelList[(int)Panels.Left].Clear();
        	processMonsters = player.hasMoved = false;
        }
        
        private void ProcessInput()
        {       
            GetInput();
        	
            ActionManager ActionManager = DungeonManager.Instance.ActionManager;
			
            processMonsters = player.hasMoved = DungeonManager.Instance.Redraw = true;
            
            //This checks until one is true as we can only have on action press.
            if (ActionManager.IsActionPushed(Actions.North))
                player.hasMoved = processMonsters = player.MovePlayer(Direction.N);
            else if (ActionManager.IsActionPushed(Actions.South))
                player.hasMoved = processMonsters = player.MovePlayer(Direction.S);
            else if (ActionManager.IsActionPushed(Actions.East))
                player.hasMoved = processMonsters = player.MovePlayer(Direction.E);
            else if (ActionManager.IsActionPushed(Actions.West))
                player.hasMoved = processMonsters = player.MovePlayer(Direction.W);
            else if (ActionManager.IsActionPushed(Actions.Jump))
            {
                DoJump();
                PanelList[(int)Panels.Bottom].Clear();
            }
            else if (ActionManager.IsActionPushed(Actions.Shoot))
            {
                ShootFireball();
                PanelList[(int)Panels.Bottom].Clear();
            }
            else if (ActionManager.IsActionPushed(Actions.Pick))
            {
                ActionHelper.Get(player.Position);
                processMonsters = player.hasMoved = false;
            }
            else if (ActionManager.IsActionPushed(Actions.Drop))
            {
                ActionHelper.Put(player.Position);
                processMonsters = player.hasMoved = false;
            }
            else if (ActionManager.IsActionPushed(Actions.Look))
            {
                DoLook();
                PanelList[(int)Panels.Bottom].Clear();
                processMonsters = player.hasMoved = false;
            }
            else if (ActionManager.IsActionPushed(Actions.Build))
            {
            	//controls menu input loop
            	ShowBuildMenu = true;
            	
            	MenuPanel mp = (MenuPanel)PanelList[(int)Panels.Left];
            	Menu menu = mp.Menu;
            	menu.Clear();
            	SetupMenu(menu);
            	
            	mp.DrawMenu();
            	while(ShowBuildMenu)
        		{
            		mp.HandleInput();
        		}
            	
            	PanelList[(int)Panels.Left].Clear();
            	PanelList[(int)Panels.Bottom].Clear();
            	DungeonManager.Instance.Redraw = true;
            }
            else if (ActionManager.IsActionPushed(Actions.Trap))
            {
            	DoTrap();
            	PanelList[(int)Panels.Bottom].Clear();
            	DungeonManager.Instance.Redraw = true;
            }
            else if (ActionManager.IsActionPushed(Actions.Open))
            {
            	DoOpen();      	
            	
            	DungeonManager.Instance.Redraw = true;
            }
            else if (ActionManager.IsActionPushed(Actions.Wait))
            {
                player.hasMoved = false;
            }
            else if (ActionManager.IsActionPushed(Actions.Quit))
            {
            	ShowQuitScreen();
                processMonsters = player.hasMoved = false;
            }
            else if (ActionManager.IsActionPushed(Actions.Help))
            {
            	ShowHelpScreen();
            	processMonsters = player.hasMoved = false;
            }
            else if (ActionManager.IsActionPushed(Actions.FollowToggle))
            {
            	Properties.Settings.Default.FollowToggle = 
            		GameManager.Instance.ToggleSetting(Properties.Settings.Default.FollowToggle);

                processMonsters = player.hasMoved = false;
            }
            else if (ActionManager.IsActionPushed(Actions.SoundToggle))
            {
                //Toggle sound
                Properties.Settings.Default.SoundToggle =
                    GameManager.Instance.ToggleSetting(Properties.Settings.Default.SoundToggle);
                
                Properties.Settings.Default.Save();
                
                GameManager.Instance.SoundManager.PlaySound(SoundList.Pluck.ToString());

                processMonsters = player.hasMoved = false;
            }
            else if (ActionManager.IsActionPushed(Actions.ToggleGodMode))
            {
                //Toggle godmode
                DungeonManager.Instance.GodMode = 
                    GameManager.Instance.ToggleSetting(DungeonManager.Instance.GodMode);

                processMonsters = player.hasMoved = false;

            }
            else if (ActionManager.IsActionPushed(Actions.ChangeAnimationSpeed))
            {
            	GameManager.Instance.ChangeAnimSpeed();

                processMonsters = player.hasMoved = false;
            }
            else
            {
                processMonsters = player.hasMoved = DungeonManager.Instance.Redraw = false;
            }
        }
        
		void DoTrap()
		{
			if(DungeonManager.Instance.DungeonLevel.SquareContainsTrap(player.Position))
			{
				Trap t = DungeonManager.Instance.DungeonLevel.GetTiles(player.Position).
					OfType<Trap>().FirstOrDefault();
			
				if (t.IsSet)
					t.Disarm();
				else
					t.Set();
			}
			else
			{
				DungeonManager.Instance.PropManager.Add(new Trap(player.Position));
			}
			
			
		}
        
		void ShowHelpScreen()
		{
			TextScreen hs = new TextScreen(DungeonManager.Instance.Texts["Help"]);
            hs.Show();
            DungeonManager.Instance.DungeonVisualizer.ForceFullVisualization(DungeonManager.Instance.DungeonLevel);
                
		}
        
		void ShowQuitScreen()
		{
			    QuitScreen s = new QuitScreen();
                if (s.ShowYesNo(this))
                {
                	Exit = true;
                }
                else
                {
                DungeonManager.Instance.DungeonVisualizer.ForceFullVisualization(DungeonManager.Instance.DungeonLevel);
                }
		}

        private void DoJump()
        {
        	//shortens calls.
            ActionManager ActionManager = DungeonManager.Instance.ActionManager;
        	
            //Forces redraw of HUD
			olduses = -1;            
        
            if (player.hasJumped)
            {	
            	PanelList[1].Clear();
            	PanelList[1].Write(Properties.Strings.Used,0, ConsoleColor.White, TextAlignment.Center);
            	processMonsters = player.hasMoved = false;
            	WaitKey();
            	PanelList[1].Clear();
            }           
            else
            {
			
            bool jump = true;
            while (jump)
            {
                Direction direction = GetDirectionFromUser();
                if (direction != Direction.Wait)
                {
                    player.hasJumped =player.hasMoved = processMonsters = player.MovePlayer(direction, Constants.JumpDistance);
                    //exits loop
                    jump = false;
                }
                else if (ActionManager.IsActionPushed(Actions.Quit) || 
                         ActionManager.IsActionPushed(Actions.Jump))
                {
                	processMonsters = player.hasMoved = jump = false;
                	
                }

            }
            PanelList[(int)Panels.Bottom].Clear();
            }
            
 
        }
       
        private void DoOpen()
        {
        	//shortens calls.
            ActionManager ActionManager = DungeonManager.Instance.ActionManager;        
			
            bool loop = true;
            while (loop)
            {
                Direction direction = GetDirectionFromUser();
                if (direction != Direction.Wait)
                {
                	
                	Point p = DungeonLevel.GetNewPosition(player.Position,direction,1);
                	Door d = DungeonManager.Instance.DungeonLevel.
                		GetTiles(p).OfType<Door>().FirstOrDefault();
                	
                		if ( d!= null)
                		{
                			d.Open();
                			processMonsters = true;
                		}
                		
                    //exits loop
                    loop = false;
                }
                else if (ActionManager.IsActionPushed(Actions.Quit) || 
                         ActionManager.IsActionPushed(Actions.Jump))
                {
                	processMonsters = player.hasMoved = loop = false;
                	
                }

            }
            PanelList[(int)Panels.Bottom].Clear();
            
 
        }
        
        public Direction GetDirectionFromUser()
        {
        	PanelList[1].Write(Properties.Strings.ChooseDirection,0, ConsoleColor.White, TextAlignment.Center);
        	GetInput();
            return GetDirection();
        }
        
        private Direction GetDirection()
        {
        	ActionManager ActionManager = DungeonManager.Instance.ActionManager;
            Direction direction;

            if (ActionManager.IsActionPushed(Actions.North))
                direction = Direction.N;
            else if (ActionManager.IsActionPushed(Actions.South))
                direction = Direction.S;
            else if (ActionManager.IsActionPushed(Actions.East))
                direction = Direction.E;
            else if (ActionManager.IsActionPushed(Actions.West))
                direction = Direction.W;
            else
                direction = Direction.Wait;

            return direction;
        }

        protected void InitializeManagers(int seed)
        {
            DungeonManager.Instance.Initialize(seed);
            DungeonManager.Instance.DungeonScreen = this;
            
            GameManager.Instance.GameTime.Restart();
        }

        private void ResetBools()
        {
            player.hasJumped = false;
            player.hasFired = false;
        }

        private void InitialDraw()
        {
            //Swapping these two lines around stopped the flicker, no idea why!
            //this draws the panels
            DungeonManager.Instance.DungeonVisualizer.ForceFullVisualization(DungeonManager.Instance.DungeonLevel);
            //this does what it says on the tin. 
            DungeonManager.Instance.ForceRedraw();
            DrawTitle();
        }

        private void DrawTitle()
        {
            PanelList[0].Clear();
            //removed seed
            PanelList[0].Write(DungeonManager.Instance.DungeonLevel.ToString(),0, ConsoleColor.White, TextAlignment.Center); 
                //+ "  Seed: " + DungeonManager.Instance.Seed.ToString());
        }

        private static void AddVictoryScreen()
        {
            //TextScreen ts;
            if (DungeonManager.Instance.PlayerHasWon.Value)
            { GameManager.Instance.ScreenStack.Push(new TextScreen(DungeonManager.Instance.Texts["Victory"])); }
            else
            { GameManager.Instance.ScreenStack.Push(new TextScreen(DungeonManager.Instance.Texts["GameOver"])); }

        }

        public void ReDraw()
        {
            drawPanels = true;
            DrawTitle();
            DoDraw();
        }

        public override void Show()
        {	
        	active = true;
        	Exit = false;
        	Clear();
            drawPanels = true;
            base.Show();
        }

		int olduses;
		bool clear;
        private void DisplayHUD()
        {
            if (DungeonManager.Instance.CreatureManager.Player.WeaponSlot != null)
            {
            	clear = false;
            	Weapon w = DungeonManager.Instance.CreatureManager.Player.WeaponSlot;
                
            	if (w.UsesRemaining != olduses)
            	{
            	StringBuilder sb = new StringBuilder(w.Name + ": ");                
                for (int i = 0; i < w.UsesRemaining; i++)
                {
                    sb.Append("*");
                }
            	
                PanelList[(int)Panels.Right].Clear();
                PanelList[(int)Panels.Right].Write(sb.ToString(),0, ConsoleColor.White,TextAlignment.Center);
            	}
                olduses = w.UsesRemaining;
            }
            else if(!clear)
            {
            	PanelList[(int)Panels.Right].ClearLine(0);
            	clear = true;
            }
            
            PanelList[(int)Panels.Right].Write(Properties.Strings.StatsMonstersKilled + ": " +
            	player.Stats.MonstersKilled.ToString(),1,ConsoleColor.White, TextAlignment.Center);

        }

        private void CheckNewTile()
        {
            Point newPosition = new Point(player.X, player.Y);

            if(!DungeonManager.Instance.GodMode)
            //Fight monster if there is one
            DungeonManager.Instance.CreatureManager.CreatureCollisions();


            //auto pick up sword
            ActionHelper.Get(newPosition);

            // auto take stairwell
            TakeStairWell(newPosition);
        }
        
        private void TakeStairWell(Point newPosition)
        {
            if (DungeonManager.Instance.DungeonLevel.GetTile(newPosition,Layers.Prop) is Stairwell)
            {
                //check for victory
                if (DungeonManager.Instance.CurrentDungeonLevel >= 5)
                {
                    DungeonManager.Instance.gameOver = true;
                }
                else
                {
                    MoveToNextLevel();
                }
            }
        }

        private void MoveToNextLevel()
        {
            player.hasJumped = false;
            player.hasFired = false;

            //no sword on next level. Murha, ha, ha, ha!!!
            //player.Inventory.Clear();
            //sword now has three uses
			
            DungeonManager.NextLevel();
            
            processMonsters = false;
            ReDraw();
        }

        private void DoLook()
        {
            bool newTile = false;
            bool look = true;
            
            Tile lookTile = DungeonManager.Instance.PropManager.LookTile;
            
            lookTile.Active = true;
            lookTile.Position = player.Position;

            while(look)
            {
            	ActionManager ActionManager = DungeonManager.Instance.ActionManager;
                GetInput();

                if (ActionManager.IsActionPushed(Actions.North))
                {lookTile.Y -= 1;newTile = true;}
                else if (ActionManager.IsActionPushed(Actions.South))
                {lookTile.Y += 1;newTile = true;}
                else if (ActionManager.IsActionPushed(Actions.East))
                {lookTile.X += 1;newTile = true;}
                else if (ActionManager.IsActionPushed(Actions.West))
                {lookTile.X -= 1;newTile = true;}
                else if (ActionManager.IsActionPushed(Actions.Quit) || 
                         ActionManager.IsActionPushed(Actions.Look))
                { 
                	look = false;
                }

                if (newTile)
                {
                    //limit

                    lookTile.Position = DungeonLevel.ClampToDungeon(lookTile.Position);

                    Tile viewTile = (Tile)DungeonManager.Instance.DungeonLevel.GetTopTile(lookTile.Position);
                    lookTile.ImageCharacter = viewTile.ImageCharacter;
                    lookTile.Description = viewTile.Description;
                    lookTile.Color = ConsoleColor.Blue;
                    lookTile.Background = ConsoleColor.DarkCyan;
		
                    string description = Properties.Strings.MysteryObject;

                    if (viewTile.Description != null)
                        description = viewTile.Description;

                    PanelList[1].Clear();
                    PanelList[1].Write(description,0, ConsoleColor.White, TextAlignment.Center);

                    DungeonManager.Instance.ForceRedraw();
                    newTile = false;
                }
            //Draw
            }
            lookTile.Active = false;
            DungeonManager.Instance.ForceRedraw();
        }

        private void ShootFireball()
        {
        	ActionManager ActionManager = DungeonManager.Instance.ActionManager;
            bool shoot = true;
            
            //Forces update of hud
            olduses = -1;
            PanelList[(int)Panels.Bottom].Clear();
            if (player.hasFired)
            {
            
            PanelList[(int)Panels.Bottom].Write(Properties.Strings.Used,0, ConsoleColor.White, TextAlignment.Center);
            processMonsters = player.hasMoved = false;
            WaitKey();
            PanelList[(int)Panels.Bottom].Clear();
            
            }
            else
            {
            	
            
            while (shoot)
            {
                Direction direction;
                direction = GetDirectionFromUser();
                if (direction != Direction.Wait && !player.hasFired)
                {
                	player.ShootFireball(direction);
                    shoot = false;
                }
                else if (ActionManager.IsActionPushed(Actions.Quit) || 
                         ActionManager.IsActionPushed(Actions.Shoot))
                   processMonsters = player.hasMoved = shoot = false;
                
            }
            PanelList[(int)Panels.Bottom].Clear();
            }
        }
        
        
        private static void ProcessFireball(Fireball fireball)
        {
        	long lastmove = 0;
        	long delay = 120;
            while (fireball.Active && DungeonManager.Instance.DungeonLevel.IsOKToMove(fireball.Position))
            {
            	if (GameManager.Instance.Time.ElapsedMilliseconds > lastmove + delay)
            	{
            		lastmove = GameManager.Instance.Time.ElapsedMilliseconds;
            	
                fireball.range--;
                //Do fireball stuff
                //DungeonManager.Instance.PropManager.Fireball
                //Fireball move
                DungeonManager.Instance.PropManager.Fireball.Move(fireball.Position, fireball.Direction, 1);
                //Fireball kill monsters
                List<Monster> monstersInBattle = 
                    DungeonManager.Instance.CreatureManager.Monsters.Where(m => m.X == fireball.X && m.Y == fireball.Y).ToList();
                foreach (Monster m in monstersInBattle)
                {
                    fireball.Kill(m);

                    DungeonManager.Instance.PlaySound(SoundList.Battle);

                    fireball.ImageCharacter = "*";

                    DungeonManager.Instance.DungeonVisualizer.Flash(fireball);

                    fireball.ImageCharacter = "o";
                    fireball.Color = ConsoleColor.Magenta;
                    fireball.Active = false;
                }
                if (fireball.range == 0)
                    fireball.Active = false;

                //Draw
                DungeonManager.Instance.ForceRedraw();

            	}
            }
            DungeonManager.Instance.PropManager.Fireball.Active = false;
            DungeonManager.Instance.Redraw = true;
        }
        
        private void resetOldUses(object sender, EventArgs e)
        {
        	olduses =-1;
        }
        
        public void SetSeed(int seed =-1)
        {
        	this.seed = seed;
        }
        
        //This is more like data
        public void SetDefaultKeys()
        {
        	ActionManager actionManager = DungeonManager.Instance.ActionManager;
        	
            actionManager.SetButtons(Actions.North, ConsoleKey.UpArrow);
            actionManager.SetButtons(Actions.South,  ConsoleKey.DownArrow);
            actionManager.SetButtons(Actions.East,  ConsoleKey.RightArrow);
            actionManager.SetButtons(Actions.West, ConsoleKey.LeftArrow);
            actionManager.SetButtons(Actions.North, ConsoleKey.NumPad8);
            actionManager.SetButtons(Actions.South, ConsoleKey.NumPad5);
            actionManager.SetButtons(Actions.East, ConsoleKey.NumPad6);
            actionManager.SetButtons(Actions.West, ConsoleKey.NumPad4);
            actionManager.SetButtons(Actions.Jump, ConsoleKey.J);
            actionManager.SetButtons(Actions.Shoot, ConsoleKey.F);
            	actionManager.SetButtons(Actions.Shoot, ConsoleKey.Z);
            actionManager.SetButtons(Actions.Pick, ConsoleKey.G); 
            actionManager.SetButtons(Actions.Drop, ConsoleKey.P); 
            	actionManager.SetButtons(Actions.Drop, ConsoleKey.D);
            actionManager.SetButtons(Actions.Build, ConsoleKey.B);
            actionManager.SetButtons(Actions.Trap, ConsoleKey.T);            
            actionManager.SetButtons(Actions.Open, ConsoleKey.O);            
            actionManager.SetButtons(Actions.Look, ConsoleKey.L);
            actionManager.SetButtons(Actions.Wait, ConsoleKey.OemPeriod);
            actionManager.SetButtons(Actions.Quit, ConsoleKey.Q);
            	actionManager.SetButtons(Actions.Quit, ConsoleKey.Escape);
            actionManager.SetButtons(Actions.Help, ConsoleKey.F1);
            actionManager.SetButtons(Actions.FollowToggle, ConsoleKey.F2);
            actionManager.SetButtons(Actions.SoundToggle, ConsoleKey.F3);
            actionManager.SetButtons(Actions.ChangeAnimationSpeed, ConsoleKey.F4);
            actionManager.SetButtons(Actions.ToggleGodMode, ConsoleKey.F9);
             
        }
        
        #endregion

    }
}
